package kizzo.game 
{
	import kizzo.game.tile_objects.*;
	import kizzo.geometry.TileCoord3D;		
	import org.flashdevelop.utils.FlashConnect;
	
	/**
	 * ...
	 * @author kizzo
	 */
	public class RandomGenerator 
	{
		private var state_array:Vector.<Vector.<Vector.<TileState>>>;
		private var dir_vectors:Vector.<TileCoord3D>;
		
		private var slave_cube_num:uint;
		private var move_num:uint;
				
		private var CHANGE_DIR_CHANCE:Number = .5;
		private var TELEPORT_CHANCE:Number = .025;
		private var CRUMBLE_CHANCE:Number;
		private var START_CRUMBLE_CHANCE:Number = 0.04;
		private var END_CRUMBLE_CHANCE:Number = .12;
		private var crumble_flag:Boolean = false;
		
		private var master_cube:GameCube;
		private var slave_cube_a:GameCube;
		private var slave_cube_b:GameCube;
		private var master_cube_vector:TileCoord3D;
		
		private var potential_move_tile_list:Vector.<TileState>;
		
		private var move_cnt:int;
		
		private var status:Object;
		
		public function RandomGenerator() 
		{
			
		}
		
		
		public function get_level():Vector.<Vector.<Vector.<TileState>>>
		{
			var vectors:Vector.<TileCoord3D> = new Vector.<TileCoord3D>();
			var selected_vector:TileCoord3D;	
			
			CRUMBLE_CHANCE = Math.random()/5;
			
			init();
			add_goals();
			set_modifiers();
			
			for (move_cnt = 50; move_cnt > 0; move_cnt--)
			{				
				vectors = get_legal_moves(master_cube, dir_vectors);
				selected_vector = choose_move(vectors);
				make_move(master_cube, selected_vector);
				
				if (status.rot == true && !(status.mod_removed == true))
				{					
					status.mod_removed = true;
					master_cube.links[0].clear_modifiers();
				}
				if (status.link == true)
				{					
					master_cube.links.shift();
				}
				clear_potential_moves();
			}
			
			return this.state_array;
		}
		
		
		private function clear_potential_moves():void
		{
			for (var i:int = 0; i < this.potential_move_tile_list.length; i++)
				potential_move_tile_list[i].potential_moves = new Vector.<PotentialMove>();
			potential_move_tile_list = new Vector.<TileState>();
		}
		
		
		private function choose_move(vectors: Vector.<TileCoord3D>):TileCoord3D
		{
			return vectors[Math.floor(Math.random() * vectors.length)];
		}
		
		
		private function make_move(cube:GameCube, v:TileCoord3D):void
		{
			var old_pos:TileCoord3D = cube.tile_coord;
			var old_state:TileState = this.state_array[old_pos.x][old_pos.y][old_pos.z];			
			
			old_state.clear_flag(cube.type);
			
			var new_pos:TileCoord3D = old_pos.add(v);
			var new_state:TileState = state_array[new_pos.x][new_pos.y][new_pos.z];
			
			if (Math.random() < START_CRUMBLE_CHANCE && !crumble_flag)
			{
				crumble_flag = true;
			}				
			else if (Math.random() < END_CRUMBLE_CHANCE && crumble_flag)
			{
				crumble_flag = false;
			}
			
			
			if (move_cnt < 30 && !(status.rot == true) && new_state.value==TileState.EMPTY)
			{
				status.rot = true;
				new_state.set_flag(TileState.MIRROR_Z);					
				//new_state.set_flag(TileState.ROTATE_CLOCKWISE_MOD);					
			}
			else if (move_cnt < 15 && !(status.link == true) && cube.links.length>0 && new_state.value==TileState.EMPTY)
			{
				status.link = true;
				new_state.set_flag(cube.links[0].link_child.link_type);					
			}				
			else if (new_state.value == TileState.EMPTY)
			{
				if (Math.random() < TELEPORT_CHANCE)
				{									
					do
					{
						var x:int = new_pos.x - 5 + Math.floor(Math.random() * 10);
						var y:int = 20;
						var z:int = new_pos.z - 5 + Math.floor(Math.random() * 10);
						
						var teleport_pos:TileCoord3D = new TileCoord3D(x, y, z);
						var teleport_state:TileState = this.state_array[x][y][z];
						FlashConnect.trace(new_pos.x_o + " ");
					}
					while (!teleport_state.is_empty())
					
					var teleport_entry:TeleportTile = new TeleportTile(null, new_pos, TileState.TELEPORT_TILE);
					teleport_entry.link = new TileCoord3D(teleport_pos.x-Game.ARRAY_OFFSET, teleport_pos.y-Game.ARRAY_OFFSET, teleport_pos.z-Game.ARRAY_OFFSET);
					
					var teleport_exit:TeleportTile = new TeleportTile(null, teleport_pos, TileState.TELEPORT_TILE);
					teleport_exit.link = new TileCoord3D(new_pos.x-Game.ARRAY_OFFSET, new_pos.y-Game.ARRAY_OFFSET, new_pos.z-Game.ARRAY_OFFSET);;
					
					FlashConnect.trace("Entry " + teleport_entry.link.to_string());
					FlashConnect.trace("Exit " + teleport_exit.link.to_string());
					
					new_state.set_flag(TileState.TELEPORT_TILE);
					new_state.add_object(teleport_entry);
					
					teleport_state.set_flag(TileState.TELEPORT_TILE);
					teleport_state.add_object(teleport_exit);
					
					new_pos = teleport_pos;
					new_state = teleport_state;					
				}				
				else
				{
					if (crumble_flag)
						new_state.set_flag(TileState.CRUMBLING_TILE);	
					else
						new_state.set_flag(TileState.SIMPLE_TILE);	
				}				
			}
			
			new_state.set_flag(cube.type);						
			cube.tile_coord = new_pos;			
			
			for (var i:int = 0; i < cube.links.length; i++)
			{
				make_move(cube.links[i].link_child, cube.links[i].modify(v));
			}
		}
		
		
		
		private function get_legal_moves(cube:GameCube, vectors:Vector.<TileCoord3D>):Vector.<TileCoord3D>
		{
			var allowed_vectors:Vector.<TileCoord3D> = new Vector.<TileCoord3D>();
			
			for (var i:int = 0; i < vectors.length; i++)
			{
				if (check_move(cube, vectors[i]) == true)
				{
					allowed_vectors.push(vectors[i]);
				}
			}

			for (var j:int = 0; j < cube.links.length; j++)
			{
				allowed_vectors = get_legal_moves(cube.links[j].link_child, apply_modifiers(cube.links[j], allowed_vectors));
			}			
			
			return allowed_vectors;
		}
		
		
		private function apply_modifiers(link:Link, vectors:Vector.<TileCoord3D>):Vector.<TileCoord3D>
		{
			var modified_vectors:Vector.<TileCoord3D> = new Vector.<TileCoord3D>();
			
			for (var i:int = 0; i < vectors.length; i++)			
				modified_vectors.push(link.modify(vectors[i]));
				
			return modified_vectors;
		}
		
		
		private function check_move(cube:GameCube, vector:TileCoord3D):Boolean
		{
			var valid:Boolean = true;
			var new_position:TileCoord3D = cube.tile_coord.add(vector);			
			
			if (new_position.x >= Game.ARRAY_SIZE || new_position.x < 0 || 
				new_position.y >= Game.ARRAY_SIZE || new_position.y < 0 || 
				new_position.z >= Game.ARRAY_SIZE || new_position.z < 0)
			{
				valid = false;
				trace(new_position.x_o + " is OOB " + Game.ARRAY_SIZE);
			}
			else
			{
				var new_state:TileState = state_array[new_position.x][new_position.y][new_position.z];
				
				if (new_state.value > 1)
				{
					//trace("occupied");
					valid = false;
				}
				else	
				for (var i:int = 0; i < new_state.potential_moves.length; i++)
				{
					if (new_state.potential_moves[i].vector.id == vector.id)
					{
						//trace("Move " + vector.to_string() + " is blocked by potential move by cube " + new_state.potential_moves[i].cube.type + " and vector " + new_state.potential_moves[i].vector.to_string());
						valid = false;
					}					
				}
			}
			
			if (valid == true)
			{
				new_state.potential_moves.push(new PotentialMove(cube, vector));
				potential_move_tile_list.push(new_state);
			}
				
			//trace("For cube " + cube.type + " allowed move " + vector.to_string() + " is " + valid);
			
			return valid;
		}
		
		
		private function init():void
		{			
			
			status = new Object();
			
			this.dir_vectors = new Vector.<TileCoord3D>();
			
			dir_vectors.push(new TileCoord3D(  1, 0, 0, 0));
			dir_vectors.push(new TileCoord3D(  0, 0,-1, 1));
			dir_vectors.push(new TileCoord3D( -1, 0, 0, 2));
			dir_vectors.push(new TileCoord3D(  0, 0, 1, 3));
			
			this.state_array = new Vector.<Vector.<Vector.<TileState>>>(Game.ARRAY_SIZE);
			
			for (var x_i:uint = 0; x_i < Game.ARRAY_SIZE; x_i++)
			{
				state_array[x_i] = new Vector.<Vector.<TileState>>(Game.ARRAY_SIZE);
				for (var y_i:uint = 0; y_i < Game.ARRAY_SIZE; y_i++)
				{		
					state_array[x_i][y_i] = new Vector.<TileState>(Game.ARRAY_SIZE);
					for (var z_i:uint = 0; z_i < Game.ARRAY_SIZE; z_i++)
					{
						state_array[x_i][y_i][z_i] = new TileState();
					}
				}
			}			
			potential_move_tile_list = new Vector.<TileState>();
		}
		
		
		private function add_goals():void
		{
			var x:int = 15 + Math.round(Math.random() * 10);
			var y:int = 20;
			var z:int = 15 + Math.round(Math.random() * 10);					
			
			var master_goal:TileState = state_array[x][y][z];		
			
			master_goal.set_flag(TileState.MASTER_GOAL);
			master_goal.set_flag(TileState.MASTER_CUBE);
			
			master_cube = new GameCube(null, new TileCoord3D(x, y, z), TileState.MASTER_CUBE);
			
			x = 15 + Math.round(Math.random() * 10);
			y = 20;
			z = 15 + Math.round(Math.random() * 10);			
			
			var slave_goal_a:TileState = state_array[x][y][z];			
			
			slave_goal_a.set_flag(TileState.SLAVE_GOAL_A);
			slave_goal_a.set_flag(TileState.SLAVE_CUBE_A);			
			
			slave_cube_a = new GameCube(null, new TileCoord3D(x, y, z), TileState.SLAVE_CUBE_A);
			
			master_cube.add_link(slave_cube_a);			
		}
		
		
		private function set_modifiers():void
		{
			master_cube.links[0].add_modifier(new MirrorModifier(new TileCoord3D(1,1,-1)));
			//master_cube.links[0].add_modifier(new RotationModifier());
			//master_cube.links[0].add_modifier(new RotationModifier());
		}

	}

}